using System;
using System.Data;
using Microsoft.CommerceServer.Catalog;
using Microsoft.CommerceServer.Inventory;
using Microsoft.CommerceServer.Catalog.Tools;
using Microsoft.CommerceServer;

namespace FindEmptyCategories
{
	public class UnitTests
	{
		private CatalogContext catalogContext;
		private CategoryTools categoryTools;

		#region Public properties
		#endregion

		public UnitTests(string siteName, bool ignoreInventorySystem)
		{
			CreateCatalogContext(siteName, ignoreInventorySystem);
			this.categoryTools = new CategoryTools(siteName, ignoreInventorySystem);
		}

		private void CreateCatalogContext(string siteName, bool ignoreInventorySystem)
		{
			CatalogSiteAgent siteAgent = new CatalogSiteAgent();
			siteAgent.SiteName = siteName;
			siteAgent.IgnoreInventorySystem = ignoreInventorySystem;
			siteAgent.AuthorizationMode = AuthorizationMode.NoAuthorization;
			this.catalogContext = CatalogContext.Create(siteAgent);
		}

		private CatalogOperationsStatus WaitForImport(ImportProgress importProgress)
		{
			CatalogOperationsStatus status = CatalogOperationsStatus.Failed;
			try
			{
				do
				{
					System.Threading.Thread.Sleep(3 * 1000);
					importProgress.Refresh();
					status = importProgress.Status;
					string info = string.Format("Operation: '{0}'. Status: '{1}': Percentage Complete: [{2}]", 
						importProgress.OperationType.ToString(), status.ToString(), 
						importProgress.PercentComplete);
					Console.WriteLine(info);
				} while (status == CatalogOperationsStatus.InProgress);

				Console.WriteLine("Catalog Import Returned: " + status.ToString());
				if (importProgress.Status != CatalogOperationsStatus.Completed)
				{
					string info = string.Format("The Import operation failed Status '{0}' ", 
						importProgress.Status.ToString());
					Console.WriteLine(info);
					int i = 0;
					foreach (CatalogError error in importProgress.Errors)
					{
						info = string.Format("Error {0}: Line# {1}- {2}", ++i, error.LineNumber, 
							error.Message);
						Console.WriteLine(info);
					}
				}
			}
			catch (Exception e)
			{
				throw e;
			}
			return status;
		}

		private CatalogOperationsStatus WaitForRebuildCatalog(RebuildProgress rebuildProgress)
		{
			CatalogOperationsStatus rebuildStatus = CatalogOperationsStatus.Failed;
			try
			{
				do
				{
					System.Threading.Thread.Sleep(3 * 1000);
					rebuildProgress.Refresh();
					rebuildStatus = rebuildProgress.Status;
					Console.WriteLine("RebuildAllCatalog Progress: " + rebuildStatus.ToString());
				} while (rebuildStatus == CatalogOperationsStatus.InProgress);

				if (rebuildProgress.Status != CatalogOperationsStatus.Completed)
				{
					string info = string.Format("The Import operation failed Status '{0}' ", 
						rebuildProgress.Status.ToString());
					Console.WriteLine(info);
					int i = 0;
					foreach (CatalogError error in rebuildProgress.Errors)
					{
						info = string.Format("Error {0}): Line# {1}- {2}", ++i, 
							error.LineNumber, error.Message);
						Console.WriteLine(info);
					}
				}
			}
			catch (Exception e)
			{
				throw e;
			}
			return rebuildStatus;
		}

		public void SetupForCatalogs()
		{
			ImportProgress progress;
			CatalogImportOptions importOptions= null;
			#region Setup
			try
			{
				importOptions = new CatalogImportOptions();
				importOptions.ImportSchemaChanges = true;
				importOptions.Mode = ImportMode.Full;
				importOptions.GenerateFullTextIndexes = true;
				//Import first base catalog
				importOptions.FileName = "Test1.xml";
				progress = this.catalogContext.ImportXml(importOptions, importOptions.FileName);
				this.WaitForImport(progress);
				//Import second base catalog
				importOptions.FileName = "Test2.xml";
				progress = this.catalogContext.ImportXml(importOptions, importOptions.FileName);
				this.WaitForImport(progress);
				//Import third base catalog
				importOptions.FileName = "Test3.xml";
				progress = this.catalogContext.ImportXml(importOptions, importOptions.FileName);
				this.WaitForImport(progress);
			}
			catch (Exception e)
			{
				Console.WriteLine("Error in setup " + e.Message);
				throw e;
			}
			#endregion
		}

		public void SetupForInventory()
		{
			ImportProgress progress;
			InventoryImportOptions importOptions = null;
			#region Setup
			try
			{
				importOptions = new InventoryImportOptions();
				importOptions.ImportSchemaChanges = true;
				importOptions.Mode = ImportMode.Full;
				//Import first inventory catalog
				importOptions.FileName = "TestInv1.xml";
				progress = this.catalogContext.InventoryContext.ImportXml(importOptions, importOptions.FileName);
				this.WaitForImport(progress);
				//Import second inventory catalog
				importOptions.FileName = "TestInv2.xml";
				progress = this.catalogContext.InventoryContext.ImportXml(importOptions, importOptions.FileName);
				this.WaitForImport(progress);
			}
			catch (Exception e)
			{
				Console.WriteLine("Error in setup " + e.Message);
				throw e;
			}
			#endregion
		}

		public bool TestBaseCatalogsWithoutInventory()
		{
			bool result = true;
			try
			{
				CatalogItemsDataSet dataset = this.categoryTools.GetEmptyCategories(
					"Adventure Works Catalog");
				//Verify the results
				if (dataset.CatalogItems.Rows.Count != 2)
				{
					Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
						+ "is incorrect Excepted 2 Actual " + dataset.CatalogItems.Rows.Count);
					result = false;
				}
				else
				{
					//Verify if the expected categories are returned
					foreach (DataRow row in dataset.CatalogItems.Rows)
					{
						if (!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains
							("SleepingBags") && !row[CatalogItemsDataSetSchema.CategoryName].ToString().
							Contains("Stoves"))
						{
							Console.WriteLine("Expected empty category for Adventure Works Catalog "
								+ "is not returned Excepted Backpack, Stoves Actual " + 
								row[CatalogItemsDataSetSchema.CategoryName]);
							result = false;
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected Exception in TestBaseCatalogsWithoutInventory " + e.Message);
				result = false;
			}
			return result;
		}

		public bool TestBaseCatalogsWithInventory()
		{
			bool result = true;
			try
			{
				//Filter inventory with default inventory site resource properties
				CatalogItemsDataSet dataset = this.categoryTools.GetEmptyCategories(
					"Adventure Works Catalog");
				//Verify the results
				if (dataset.CatalogItems.Rows.Count != 2)
				{
					Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
						+ "is incorrect Excepted 2 Actual " + dataset.CatalogItems.Rows.Count);
					result = false;
				}
				else
				{
					//Filter inventory for OOS products
					InventoryFilterOptions filterOptions = new InventoryFilterOptions();
					filterOptions.FilterOutOfStockSkus = true;
					dataset = this.categoryTools.GetEmptyCategories(
						"Adventure Works Catalog",filterOptions);
					if (dataset.CatalogItems.Rows.Count != 3)
					{
						Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
							+ "is incorrect Excepted 3 Actual " + dataset.CatalogItems.Rows.Count);
						result = false;
					}
					else
					{
						//Verify if the expected categories are returned
						foreach (DataRow row in dataset.CatalogItems.Rows)
						{
							if (!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains
								("SleepingBags") && !row[CatalogItemsDataSetSchema.CategoryName].ToString().
								Contains("Stoves") && !row[CatalogItemsDataSetSchema.CategoryName].
									ToString().Contains("Carabiner"))
							{
								Console.WriteLine("Expected empty category for Adventure Works Catalog "
									+ "is not returned Excepted Backpack, Stoves Actual " +
									row[CatalogItemsDataSetSchema.CategoryName]);
								result = false;
							}
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected Exception in TestBaseCatalogsWithoutInventory " + e.Message);
				result = false;
			}
			return result;
		}

		public bool TestVirtualCatalogsWithoutInventory()
		{
			bool result = true;
			string vcName = "VC";
			#region Setup
			try
			{
				VirtualCatalog vc = this.catalogContext.CreateVirtualCatalog(vcName,
					"en-US", "en-US");
				vc.AddVirtualCatalogRule("Adventure Works Catalog", null, null, null, false);
				vc.Save();
				this.WaitForRebuildCatalog(vc.Rebuild());
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected exception in setup for TestVirtualCatalogsWithoutInventory"
					+ e.Message);
				throw e;
			}
			#endregion
			try
			{
				CatalogItemsDataSet dataset = this.categoryTools.GetEmptyCategories(vcName);
				//Verify the results
				if (dataset.CatalogItems.Rows.Count != 2)
				{
					Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
						+ "is incorrect Excepted 2 Actual " + dataset.CatalogItems.Rows.Count);
					result = false;
				}
				else
				{
					//Verify if the expected categories are returned
					foreach (DataRow row in dataset.CatalogItems.Rows)
					{
						if (!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains
							("SleepingBags") && 
							!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains("Stoves"))
						{
							Console.WriteLine("Expected empty category for Adventure Works Catalog "
								+ "is not returned Excepted Backpack, Stoves Actual " +
								row[CatalogItemsDataSetSchema.CategoryName]);
							result = false;
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected Exception in TestBaseCatalogsWithoutInventory " + e.Message);
				result = false;
			}
			return result;
		}

		public bool TestVirtualCatalogsWithInventory()
		{
			bool result = true;
			//Note: Inventory Catalog is the same as that for the base catalog i.e. no explicit mapping for VC
			try
			{
				//Filter inventory for OOS products
				InventoryFilterOptions filterOptions = new InventoryFilterOptions();
				filterOptions.FilterOutOfStockSkus = true;
				CatalogItemsDataSet dataset = this.categoryTools.GetEmptyCategories(
					"VC", filterOptions);
				if (dataset.CatalogItems.Rows.Count != 3)
				{
					Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
						+ "is incorrect Excepted 3 Actual " + dataset.CatalogItems.Rows.Count);
					result = false;
				}
				else
				{
					//Verify if the expected categories are returned
					foreach (DataRow row in dataset.CatalogItems.Rows)
					{
						if (!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains
							("SleepingBags") && !row[CatalogItemsDataSetSchema.CategoryName].ToString().
							Contains("Stoves") && !row[CatalogItemsDataSetSchema.CategoryName].
								ToString().Contains("Carabiner"))
						{
							Console.WriteLine("Expected empty category for Adventure Works Catalog "
								+ "is not returned Excepted Backpack, Stoves Actual " +
								row[CatalogItemsDataSetSchema.CategoryName]);
							result = false;
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected Exception in TestBaseCatalogsWithoutInventory " + e.Message);
				result = false;
			}
			return result;
		}

		public bool TestVirtualCatalogsWithHierarchyChangesWithoutInventory()
		{
			bool result = true;
			string vcName = "TestVC1";
			#region Setup
			try
			{
				VirtualCatalog vc = this.catalogContext.CreateVirtualCatalog(vcName,
					"en-US", "en-US");
				vc.AddVirtualCatalogRule("TestBC1", null, null, null, false);
				vc.AddVirtualCatalogRule("TestBC2", null, null, null, false);
				vc.Save();
				this.WaitForRebuildCatalog(vc.Rebuild());
				vc.Refresh();
				//Adding more hierarchy to the VC to make two more catgegories non-empty
				StaticCategory category = (StaticCategory)vc.GetCategory("Category11(TestBC2)");
				category.AddChildCategory("Category111(TestBC1)");
				category.Save();
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected exception in setup for TestVirtualCatalogsWithoutInventory"
					+ e.Message);
				throw e;
			}
			#endregion
			try
			{
				CatalogItemsDataSet dataset = this.categoryTools.GetEmptyCategories(vcName);
				//Verify the results
				if (dataset.CatalogItems.Rows.Count != 2)
				{
					Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
						+ "is incorrect Excepted 2 Actual " + dataset.CatalogItems.Rows.Count);
					result = false;
				}
				else
				{
					//Verify if the expected categories are returned
					foreach (DataRow row in dataset.CatalogItems.Rows)
					{
						if (!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains
							("Category2(TestBC1)") &&
							!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains("Category21(TestBC1)"))
						{
							Console.WriteLine("Expected empty category for Adventure Works Catalog "
								+ "is not returned Excepted Category2(TestBC1), Category21(TestBC1) Actual " +
								row[CatalogItemsDataSetSchema.CategoryName]);
							result = false;
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected Exception in TestBaseCatalogsWithoutInventory " + e.Message);
				result = false;
			}
			return result;
		}

		public bool TestVirtualCatalogsWithHierarchyChangesWithInventory()
		{
			bool result = true;
			//Note: Inventory Catalog is the not same as that for the base catalog 
			//i.e. explicit mapping for VC
			try
			{
				//Filter inventory for OOS products
				InventoryFilterOptions filterOptions = new InventoryFilterOptions();
				filterOptions.FilterBackorderableSkus = true;
				filterOptions.FilterOutOfStockSkus = true;
				CatalogItemsDataSet dataset = this.categoryTools.GetEmptyCategories(
					"TestVC1", filterOptions);
				if (dataset.CatalogItems.Rows.Count != 6)
				{
					Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
						+ "is incorrect Excepted 8 Actual " + dataset.CatalogItems.Rows.Count);
					result = false;
				}
				filterOptions.FilterBackorderableSkus = false;
				filterOptions.FilterPreorderableSkus = true;
				dataset = this.categoryTools.GetEmptyCategories(
					"TestVC1", filterOptions);
				if (dataset.CatalogItems.Rows.Count != 4)
				{
					Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
						+ "is incorrect Excepted 4 Actual " + dataset.CatalogItems.Rows.Count);
					result = false;
				}

			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected Exception in TestBaseCatalogsWithoutInventory " + e.Message);
				result = false;
			}
			return result;
		}

		public bool TestVCViewWithoutInventory()
		{
			bool result = true;
			string vcName = "VCView";
			#region Setup
			try
			{
				VirtualCatalog vc = this.catalogContext.CreateVirtualCatalog(vcName,
					"en-US", "en-US");
				vc.AddVirtualCatalogRule("VC", null, null, null, false);
				vc.Save();
				this.WaitForRebuildCatalog(vc.Rebuild());
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected exception in setup for TestVirtualCatalogsWithoutInventory"
					+ e.Message);
				throw e;
			}
			#endregion
			try
			{
				CatalogItemsDataSet dataset = this.categoryTools.GetEmptyCategories(vcName);
				//Verify the results
				if (dataset.CatalogItems.Rows.Count != 2)
				{
					Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
						+ "is incorrect Excepted 2 Actual " + dataset.CatalogItems.Rows.Count);
					result = false;
				}
				else
				{
					//Verify if the expected categories are returned
					foreach (DataRow row in dataset.CatalogItems.Rows)
					{
						if (!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains
							("SleepingBags") &&
							!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains("Stoves"))
						{
							Console.WriteLine("Expected empty category for Adventure Works Catalog "
								+ "is not returned Excepted Backpack, Stoves Actual " +
								row[CatalogItemsDataSetSchema.CategoryName]);
							result = false;
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected Exception in TestBaseCatalogsWithoutInventory " + e.Message);
				result = false;
			}
			return result;
		}

		public bool TestVCViewWithInventory()
		{
			bool result = true;
			//Note: Inventory Catalog is the same as that for the base catalog i.e. no explicit mapping for VC
			try
			{
				//Filter inventory for OOS products
				InventoryFilterOptions filterOptions = new InventoryFilterOptions();
				filterOptions.FilterOutOfStockSkus = true;
				CatalogItemsDataSet dataset = this.categoryTools.GetEmptyCategories(
					"VCView", filterOptions);
				if (dataset.CatalogItems.Rows.Count != 3)
				{
					Console.WriteLine("Number of empty categories returned for Adventure Works Catalog "
						+ "is incorrect Excepted 3 Actual " + dataset.CatalogItems.Rows.Count);
					result = false;
				}
				else
				{
					//Verify if the expected categories are returned
					foreach (DataRow row in dataset.CatalogItems.Rows)
					{
						if (!row[CatalogItemsDataSetSchema.CategoryName].ToString().Contains
							("SleepingBags") && !row[CatalogItemsDataSetSchema.CategoryName].ToString().
							Contains("Stoves") && !row[CatalogItemsDataSetSchema.CategoryName].
								ToString().Contains("Carabiner"))
						{
							Console.WriteLine("Expected empty category for Adventure Works Catalog "
								+ "is not returned Excepted Backpack, Stoves Actual " +
								row[CatalogItemsDataSetSchema.CategoryName]);
							result = false;
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Unexpected Exception in TestBaseCatalogsWithoutInventory " + e.Message);
				result = false;
			}
			return result;
		}
	}
}
